#!/usr/bin/env python
# -*- coding: utf-8 -*-

import random
import base64
import string
import os
from M2Crypto.EVP import Cipher as CP
from conf.yaml_tool import yaml_load, yaml_dump
from conf.jinja2_tool import jinja2_dump
from k8s.env import TEMPLATES_DIR, CONF_DIR
import hashlib
from kubernetes import client, config
from k8s.env import KEYS, KEYS_DIR
from log.logger import logger
from misc.color import INFO, SINFO, CRIT, SUMM, WARN
from misc.exec_cmd import safe_exec_cmd
import commands

# do not change it!
_ESK = ''.join(["T", "M", "0", "N", "A", "j", "y", "B", "C", "K",
                "4", "t", "b", "c", "I", "L", "N", "u", "5", "t",
                "B", "0", "l", "Z", "A", "i", "t", "M", "w", "3",
                "F", "t", "b", "J", "8", "Y", "9", "K", "Y", "l",
                "R", "M", "d", "V", "x", "T", "o", "p", "q", "8",
                "y", "t", "o", "0", "x", "h", "A", "4", "K", "s",
                "Q", "0", "R", "C", "e", "F", "7", "W", "Q", "R",
                "l", "y", "t", "v", "o", "v", "S", "D", "V", "F",
                "3", "h", "X", "p", "s", "W", "R", "s", "l", "v",
                "8", "k", "a", "l", "V", "X", "X", "l", "D", "G",
                "u", "9", "w", "9", "S", "R", "J", "S", "0", "h",
                "O", "I", "+", "/", "+", "v", "w", "2", "T", "Z",
                "2", "W", "1", "E", "R", "b", "T", "S"][15:57])


def _ECP(data, key, iv=None):
    """ encrypt a string
    @param key - secret key, 32 bytes len random string
    @param iv - initial vector, 16 bytes len ransom string
    @param data - the target string
    @return encrypted string in base64
    """
    if iv is None:
        iv = '\0' * 16

    try:
        cipher = CP(''.join(["a", "e", "s", "_", "1", "2", "8", "_", "c", "b", "c"]), key, iv, 1,
                    key_as_bytes=1, d=''.join(["s", "h", "a", "1"]), salt=''.join(["9", "8", "3", "7", "4", "8", "7", "2"]))
        v = cipher.update(data)
        v += cipher.final()
        del cipher
    except Exception as e:
        # logger.exception("_ECP failed: %s" % e)
        return None
    return v

def _DCP(data, key, iv=None):
    """ decrypt a string
    @param key - secret key, 32 bytes len random string
    @param iv - initial vector, 16 bytes len ransom string
    @param data - the encrypted string by encrypt_string()
    @return decrypted string
    """
    if iv is None:
        iv = '\0' * 16
        # pdb.set_trace()
    try:
        cipher = CP(''.join(["a", "e", "s", "_", "1", "2", "8", "_", "c", "b", "c"]), key, iv, 0,
                    key_as_bytes=1, d=''.join(["s", "h", "a", "1"]), salt=''.join(["9", "8", "3", "7", "4", "8", "7", "2"]))
        v = cipher.update(data)
        v += cipher.final()
        del cipher
    except Exception as e:
        # logger.exception("_DCP failed: %s" % e)
        return None
    return v

def _alloc_access_key_id():
    """ allocate a new access key id """
    key_id = ""
    for _ in range(0, 20):
        key_id += random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
    return key_id

def _alloc_secret_access_key():
    """ allocate a new secret access key """
    secret_key = ""
    for _ in range(0, 40):
        secret_key += random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012356789')
    return secret_key

def _encode_string(s):
    """ encode a string """
    return base64.b64encode(_ECP(s, _ESK))

def _decode_string(s):
    """ decode a string """
    return _DCP(base64.b64decode(s), _ESK)

def _gen_key(name):
    if os.path.exists(KEYS[name]['key_id_file']):
        with open(KEYS[name]['key_id_file']) as f:
            key_id = f.read().strip()
        with open(KEYS[name]['access_secret_key_file']) as f:
            secret_key = f.read().strip()
        with open(KEYS[name]['secret_access_key_file'], 'rb') as f:
            secret_key_encoded = f.read().strip()
    else:
        key_id = _alloc_access_key_id()
        secret_key = _alloc_secret_access_key()
        secret_key_encoded = _encode_string(secret_key)
        if not os.path.exists(KEYS_DIR):
            os.makedirs(KEYS_DIR)
        with open(KEYS[name]['key_id_file'], "w") as f:
            f.write(key_id)
        with open(KEYS[name]['access_secret_key_file'], 'w') as f:
            f.write(secret_key)
        with open(KEYS[name]['secret_access_key_file'], 'wb') as f:
            f.write(secret_key_encoded)

    return {
        "key_id": key_id,
        "secret_key": secret_key,
        "encoded_key": secret_key_encoded,
    }


def _gen_pgpass():
    pgpass_file = os.path.join(KEYS_DIR, ".pgserver_passwd")
    if os.path.exists(pgpass_file):
        with open(pgpass_file) as f:
            pgpass = f.read().strip()
    else:
        pgpass = ''.join(random.sample(string.ascii_letters + string.digits, 8))
    pgpass_md5 = hashlib.md5(pgpass + "yunify").hexdigest()
    safe_exec_cmd("/kellan/build/builder/encode_pg_passwd.py -e -s %s" % pgpass)
    encoded_pgpass = safe_exec_cmd("cat /tmp/.result.txt")['out']
    return {
        "pgpass": pgpass,
        "pgpass_encoded": encoded_pgpass,
        "pool_passwd": "'yunify:%s'" % pgpass_md5,
    }


# 生成密钥的secrets的yaml文件如console_key.yaml
def gen_key_secrets(name, zone_id):
    msg = "Building yaml for secrets [%s], namespace: [%s] ..." % (name, zone_id)
    logger.info(msg)
    print(INFO % ("|--Info: " + msg))
    keys = _gen_key(name)
    configuration = {}
    configuration["name"] = name
    configuration["zone_id"] = zone_id
    configuration["keys"] = keys
    secrets_path = os.path.join(CONF_DIR, "%s/secrets/%s.yaml"%(zone_id, name))
    jinja2_dump(TEMPLATES_DIR, "secrets.yaml.j2", configuration, secrets_path)


# 生成pg密码的secrets的yaml文件pgpass.yaml
def gen_pass_secrets(zone_id):
    msg = "Building yaml for secrets [pgpass], namespace: [%s] ..." % zone_id
    logger.info(msg)
    print(INFO % ("|--Info: " + msg))
    pgpass = _gen_pgpass()
    configuration = {}
    configuration["name"] = "pgpass"
    configuration["zone_id"] = zone_id
    configuration["keys"] = pgpass
    secrets_path = os.path.join(CONF_DIR, "%s/secrets/pgpass.yaml"%(zone_id,))
    jinja2_dump(TEMPLATES_DIR, "secrets.yaml.j2", configuration, secrets_path)


class SecretClient(object):
    def __init__(self, namespace):
        from log.logger import logger
        self.logger = logger
        self.namespace = namespace
        self.ctx = client.CoreV1Api()

    def create(self, name=None, component=None):
        '''
            name should be the yaml file name prefix, e.g. pgpass/console_key/...
        '''

        if not component:
            msg = "Namespace [%s]: creating Secrets [%s]" % (self.namespace, name)
            self.logger.info(msg)
            print(SINFO % ("|--Info: " + msg))
            yaml_file = os.path.join(CONF_DIR, "%s/secrets/%s.yaml" % (self.namespace, name))
        else:
            msg = "Namespace [%s]: creating Secrets for component [%s]" % (self.namespace, component)
            self.logger.info(msg)
            print(SINFO % ("|--Info: " + msg))
            yaml_file = os.path.join(CONF_DIR, "%s/%s/secret.yaml" % (self.namespace, component))

        if os.path.getsize(yaml_file) == 0:
            msg = "skip empty file %s" % yaml_file
            self.logger.error(msg)
            print(WARN % ("  |--Warn: " + msg))
            return

        body = yaml_load(yaml_file)
        try:
            resp = self.ctx.create_namespaced_secret(body=body, namespace=self.namespace)
            msg = "Namespace [%s]: Secrets created. name='%s'" % (self.namespace, resp.metadata.name)
            self.logger.info(msg)
            print(INFO % ("  |--Info: " + msg))
        except Exception as e:
            # conflict exception: 409
            if str(e).split()[0] != '(409)':
                msg = str(e)
                print(CRIT % ("  |--Error: " + msg))
            else:
                msg = "instance already exits."
                print(INFO % ("  |--Info: " + msg))
            self.logger.critical(msg)

    def update(self, name=None, component=None):
        '''
            name should be the yaml file name prefix, e.g. pgpass/console_key/...
        '''

        if not component:
            msg = "Namespace [%s]: creating Secrets [%s]" % (self.namespace, name)
            self.logger.info(msg)
            print(SINFO % ("|--Info: " + msg))
            yaml_file = os.path.join(CONF_DIR, "%s/secrets/%s.yaml" % (self.namespace, name))
        else:
            msg = "Namespace [%s]: creating Secrets for component [%s]" % (self.namespace, component)
            self.logger.info(msg)
            print(SINFO % ("|--Info: " + msg))
            yaml_file = os.path.join(CONF_DIR, "%s/%s/secret.yaml" % (self.namespace, component))

        if os.path.getsize(yaml_file) == 0:
            msg = "skip empty file %s" % yaml_file
            self.logger.error(msg)
            print(WARN % ("  |--Warn: " + msg))
            return

        msg = "Namespace [%s]: updating Secret [%s]" % (self.namespace, component)
        self.logger.info(msg)
        print(SINFO % ("|--Info: " + msg))

        status, msg = commands.getstatusoutput("export KUBECONFIG=/root/.kube/config; kubectl -n %s apply -f %s" % (self.namespace, yaml_file))
        if status == 0:
            self.logger.info(msg)
            print(INFO % ("  |--Info: " + msg))
        else:
            self.logger.error(msg)
            print(CRIT % ("  |--Error: " + msg))
